package com.hj.gateway.config;

import com.alibaba.csp.sentinel.adapter.gateway.common.SentinelGatewayConstants;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiDefinition;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiPathPredicateItem;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.GatewayApiDefinitionManager;
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayFlowRule;
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayParamFlowItem;
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayRuleManager;
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import jakarta.annotation.PostConstruct;
import org.springframework.context.annotation.Configuration;

import java.util.HashSet;
import java.util.Set;

/**
 * @author hj
 * @Date 2023/6/1
 * @Description:
 */
@Configuration
public class GatewayRuleConfig {

    @PostConstruct
    public void doInit() {
        // Prepare some gateway rules and API definitions (only for demo).
        // It's recommended to leverage dynamic data source or the Sentinel dashboard to push the rules.
        initCustomizedApis();
        initGatewayRules();
    }

    private void initCustomizedApis() {
        Set<ApiDefinition> definitions = new HashSet<>();
        ApiDefinition api1 = new ApiDefinition("authority_api")
                .setPredicateItems(new HashSet<>() {{
                    add(new ApiPathPredicateItem().setPattern("/sys/**"));
                    add(new ApiPathPredicateItem().setPattern("/remote/**")
                            .setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX));
                }});
        ApiDefinition api2 = new ApiDefinition("storage_api")
                .setPredicateItems(new HashSet<>() {{
                    add(new ApiPathPredicateItem().setPattern("/**")
                            .setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX));
                }});
        definitions.add(api1);
        definitions.add(api2);
        GatewayApiDefinitionManager.loadApiDefinitions(definitions);
    }

    private void initGatewayRules() {
        Set<GatewayFlowRule> rules = new HashSet<>();
        // resource API名称 (Route ID 或者 API管理里面配置的 apiName)
        rules.add(new GatewayFlowRule("authority")
                // 默认就是Route ID 0 还有个 API分组 1
                .setResourceMode(SentinelGatewayConstants.RESOURCE_MODE_ROUTE_ID)
                // 限流阈值类型（1 代表 QPS,0 代表并发线程数）,默认值QPS
                .setGrade(1)
                // QPS-阈值 或者 线程数-阈值
                .setCount(2)
                .setBurst(3)
                // QPS模式下 间隔 默认单位是 秒
                .setIntervalSec(1)

        );

        rules.add(new GatewayFlowRule("authority")
                // 默认就是Route ID 0 还有个 API分组 1
                .setResourceMode(SentinelGatewayConstants.RESOURCE_MODE_ROUTE_ID)
                // 限流阈值类型（1 代表 QPS,0 代表并发线程数）,默认值QPS
                .setGrade(0)
                // QPS-阈值 或者 线程数-阈值
                .setCount(2)
                .setBurst(3)

        );

        rules.add(new GatewayFlowRule("storage")
                .setCount(1)
                .setIntervalSec(1)
                .setBurst(1)
                // 匀速排队
                .setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER)
                // 超时时间
                .setMaxQueueingTimeoutMs(100)
                // 参数限流配置 若不提供,则代表不针对参数进行限流,该网关规则将会被转换成普通流控规则;否则会转换成热点规则
                .setParamItem(new GatewayParamFlowItem()
                        // 针对请求属性 参数属性 Header
                        .setParseStrategy(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_HEADER)
                        .setFieldName("hj-sentinel-flag")
                )
        );

        rules.add(new GatewayFlowRule("storage")
                .setCount(1)
                .setIntervalSec(1)
                .setBurst(1)
                // 快速失败
                .setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_DEFAULT)
        );

        rules.add(new GatewayFlowRule("authority_api")
                // API分组
                .setResourceMode(SentinelGatewayConstants.RESOURCE_MODE_CUSTOM_API_NAME)
                // QPS阈值
                .setCount(2)
                .setIntervalSec(1)
                .setParamItem(new GatewayParamFlowItem()
                        // 针对请求参数 参数属性 URL参数
                        .setParseStrategy(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_URL_PARAM)
                        // URL参数名称
                        .setFieldName("hj-authority_api")
                )
        );

        rules.add(new GatewayFlowRule("storage_api")
                // API分组
                .setResourceMode(SentinelGatewayConstants.RESOURCE_MODE_CUSTOM_API_NAME)
                // QPS阈值
                .setCount(1)
                // 间隔 1秒
                .setIntervalSec(1)
        );
        GatewayRuleManager.loadRules(rules);
    }


}


/*


GatewayFlowRule

       1- resource: 资源名称,可以是网关中的route名称或者用户自定义的API分组名称.
        new GatewayFlowRule("authority")
       2- resourceMode: 资源模型,限流规则则是针对API Gateway的 route（RESOURCE_MODE_ROUTE_ID）
         还是用户在Sentinel 中定义的API分组（RESOURCE_MODE_CUSTOM_API_NAME）,默认route.
        .setResourceMode(SentinelGatewayConstants.RESOURCE_MODE_ROUTE_ID)
       3- grade：限流阈值类型（1代表 QPS,0 代表并发线程数）,默认值QPS
       4- count:限流阈值.
       5- intervalSec： 统计时间窗口,单位是秒, 默认是1 秒.　
       6- controlBehavior: 流量整形的控制效果,同限流规则的controlBehavior字段,目前支持快速失败和匀速排队两种模式,默认快速失败.
       7- burst: 应对突发请求时额外允许的请求数目.
       8- maxQueueingTimeoutMs：匀速排队模式下的最长排队时间,单位是毫秒,仅在匀速排队模式下生效.
       9- paramItem: 参数限流配置.若不提供,则代表针对参数进行限流,该网关规则将会被转换成普通流控规则；否则会转换热点规则.其中的字段如下.
       10- parseStrategy: 从请求中提取参数的策略,目前支持提取来源IP(PARAM_PARSE_STRATEGY_CLIENT_IP),
           Host(PARAM_PARSE_STRATEGY_HOST),任意Header(PARAM_PARSE_STRATEGY_HEADER)
           和任意URL 参数（PARAM_PARSE_STRATEGY_URL_PARAM）四种模式.

        fieldName：若提取策略选择Header模式或者URL参数模式,则需要指定对应的Header名称或URL参数名称.

        pattern和matchStrategy:

        网关限流规则的加载可以通过GatewayRuleManager.loadRules(rules); 的方式手动加载,
         也可以通过GatewayRuleManager.register2Property(property)注册动态限流规则（建议使用这种动态限流规则的方式）


 */